Data Wrangling
Bevor wir mit den Visualisierungen und dem Erstellen der Modelle
beginnen können, müssen wir die Daten säubern. Das heisst es sollte
keine Duplikate geben, fehlende Werte sollten korrekt eingetragen werden
und Daten, die nicht verwendet werden, sollten gelöscht werden.
Es hat “N/A” Werte in den Spalten “Year” und “Publisher”. Diese Werte
sollten korrekte “NA” Werte sein, damit sie bei den Visualisierungen und
Berechnungen nicht berücksichtigt werden.
# Show rows with "N/A" values
df[grep("N/A", df$Publisher),]
df[grep("N/A", df$Year),]
# Replace "N/A" with "NA"
df[df == "N/A"] <- NA
df <- df %>%
filter(df$Global_Sales > 0.1)
df
# Check if values have been converted
df %>%
summarize(across(everything(), ~sum(is.na(.))))
Da 2017 nur 3 Einträge und 2020 nur 1 Eintrag beinhaltet, werden wir
diese Jahren nicht berücksichtigen und aus dem Dataframe löschen. Da
diese nicht vollständig sind, könnten unsere Modelle ungenau werden.
# Remove years 2017 and 2020 from dataset
df_clean <- df[!(df$Year == "2017" | df$Year == "2020"),]
# Remove unnecessary columns because they are not needed for our thesis
drop <- c("Rank")
df_clean <- df_clean[,!(names(df_clean) %in% drop)]
df_clean
# Set data to correct type
df_clean$Genre <- as.factor(df_clean$Genre)
df_clean$Year <- as.numeric(df_clean$Year)
Erste Plots erstellen
Wir erstellen ein paar erste Plots um uns einen Überblick über die
Daten zu verschaffen.
na <- sum(df_clean[, 'NA_Sales'], na.rm = TRUE)
eu <- sum(df_clean[, 'EU_Sales'], na.rm = TRUE)
jp <- sum(df_clean[, 'JP_Sales'], na.rm = TRUE)
o <- sum(df_clean[, 'Other_Sales'], na.rm = TRUE)
g <- sum(df_clean[, 'Global_Sales'], na.rm = TRUE)
fig <- plot_ly(
y = c(na, eu, jp, o),
x = c("North America", "Europe", "Japan", "Other"),
type = 'bar',
width = 800
)
fig <- fig %>% layout(title = "Video Game Sales Overview",
xaxis = list(title = "Region"),
yaxis = list(title = "Sales (million)"))
fig
Anhand dieses Diagrammes, kann man sehen, dass Nordamerika mit
Abstand der grösste Absatzmarkt im Game-Bereich ist.
Nun wollen wir noch die Verteilung der verschiedenen Spielgenres
besser sehen:
# Group by genre and summarize game sales to each region
df_genre <- df_clean %>%
group_by(Genre) %>%
summarize(
NA_Sales_Sum = sum(NA_Sales),
EU_Sales_Sum = sum(EU_Sales),
JP_Sales_Sum = sum(JP_Sales),
Other_Sales_Sum = sum(Other_Sales),
Global_Sales_Sum = sum(Global_Sales)
)
# Plot grouped bar chart video game sales by genre
fig <- plot_ly(
df_genre, y = ~Genre, x = ~NA_Sales_Sum, type = "bar", name = "North America", width = 1000, height = 800) %>%
add_trace(x = ~EU_Sales_Sum, name = "Europe") %>%
add_trace(x = ~JP_Sales_Sum, name = "Japan") %>%
add_trace(x = ~Other_Sales_Sum, name = "Other") %>%
layout(
title = "Video Game Sales by Genre",
xaxis = list(title = "Sales (million)"),
barmode = "group"
)
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Hier sieht man gut, dass Japan im Vergleich zu den anderen Märkten
viel mehr Role-Play und Strategie Spiele verkauft. Dafür werden in Japan
aber viel weniger Shooter und Action Spiele als den anderen Regionen
verkauft.
df_year <- df_clean %>%
group_by(Year) %>%
summarize(
NA_Sales_Sum = sum(NA_Sales),
EU_Sales_Sum = sum(EU_Sales),
JP_Sales_Sum = sum(JP_Sales),
Other_Sales_Sum = sum(Other_Sales),
Global_Sales_Sum = sum(Global_Sales)
)
fig <- plot_ly(
df_year, y = ~NA_Sales_Sum, x = ~Year, type = "bar", name = "North America", width = 900, height = 500) %>%
add_trace(y = ~EU_Sales_Sum, name = "Europe") %>%
add_trace(y = ~JP_Sales_Sum, name = "Japan") %>%
add_trace(y = ~Other_Sales_Sum, name = "Other") %>%
layout(
title = "Video Game from Sales by Year",
xaxis = list(title = "Year"),
yaxis = list(title = "Sales (million)"),
barmode = "stack"
)
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Überraschenderweise, sieht man in diesem Diagramm, dass die
Videospiel Verkäufe zwischen den Jahren 2005 und 2012 geboomt haben.
Danach ist die Kurve wieder abgeflacht. Dies könnte unter anderem daran
liegen, dass in dieser Zeit viele neue Konsolen auf den Markt gekommen
sind, welche grosse Neuerungen mit sich brachten. Die Playstation 3, die
Xbox 360 und die Nintendo Wii sind alle in 2005-2006 auf den Markt
gekommen und haben den Spielemarkt anscheinend stark beeinflusst.
# Calculate the average of sales of each genre from each region
df_sales_avg <- df_clean %>%
group_by(Genre) %>%
summarise(
EU_Sales_Avg = mean(EU_Sales),
NA_Sales_Avg = mean(NA_Sales),
JP_Sales_Avg = mean(JP_Sales),
Other_Sales_Avg = mean(Other_Sales),
Global_Sales_Avg = mean(Global_Sales))
df_sales_avg
Hier sehen wir die durchschnittliche Anzhal von Verkäufe pro
Genre.
Regressionsmodelle
Da wir unsere Daten jetzt besser verstehen, können wir mit den
Regressionsmodellen und mit der Beantwortung unserer Fragestellung
beginnen. Allerdings mussten wir feststellen, dass unsere Fragestellung
ungeschickt war und wir diese nicht ausreichend beantworten können.
Daher haben wir uns folgende Alternativ-Fragestellung überlegt:
Kann man anhand der nordamerikanischen Verkäufe voraussagen, wie
sich ein Genre im europäischen Markt verkaufen wird?
Diese Frage könnte einer Firma dabei helfen zu entscheiden, wieviel
Werbebudget diese in Europa ausgeben soll, nachdem ein Spiel in Amerika
bereits auf den Markt gekommen ist.
Daten optimieren
In unserem Datensatz gibt es einige Ausreisser. Zum Beispiel gibt es
Spiele, welche nur in einzelnen Regionen auf den Markt gekommen sind und
die in anderen Ländern garnicht verkauft wurden. Diese Tatsache würde
beim Erstellen der Modelle zu grossen Abweichungen und Ungenauigkeiten
führen. Daher berücksichtigen wir nur die Games, welche über 1 Mio.
Verkäufe haben.
Unten haben wir die Genres mit den besten Fits genommen, da bei den
anderen Spielgenres der r-squared Wert unter 50% lag. Das bedeutet, dass
es dort keinen Zusammenhang gibt, weshalb wir diese ebenfalls nicht
beachten werden.
Wir erstellen das Dataframe mit allen Racing Games:
# Create DataFrame only with Racing games
df_racing <- df_clean %>%
filter(
Genre == "Racing",
NA_Sales > 1.00,
EU_Sales > 1.00
)
#create scatterplot
ggplot(df_racing, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Racing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_racing <- lm(EU_Sales ~ NA_Sales, data = df_racing)
# Extract model score
mdl_racing %>%
glance() %>%
pull(r.squared)
[1] 0.8686435
# Predict EU Sales for a Racing Game based on NA Sales
predict_racing <- tibble(NA_Sales = 5)
predict(mdl_racing, predict_racing)
1
3.891789
Wenn ein Racing Game in Nord Amerika 5 Millionen Verkäufe aufweist,
liegt die Verkaufs-Vorhersage für Europa bei rund 3.9 Millionen. In
Amerika sind diese Spiele also beliebter als bei uns in Europa.
# Create DataFrame only with Role-Playing games
df_role <- df_clean %>%
filter(
Genre == "Role-Playing",
NA_Sales > 1.00,
EU_Sales > 1.00
)
#create scatterplot
ggplot(df_role, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Role-Playing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_rp <- lm(EU_Sales ~ NA_Sales, data = df_role)
# Extract model score
mdl_rp %>%
glance() %>%
pull(r.squared)
[1] 0.8433709
# Predict EU Sales for a Role-Playing Game based on NA Sales
predict_rp <- tibble(NA_Sales = 5)
predict(mdl_rp, predict_rp)
1
3.451286
# Create DataFrame only with Shooter games
df_shooter <- df_clean %>%
filter(
Genre == "Shooter",
NA_Sales > 0.00,
EU_Sales > 0.00
)
# Create DataFrame only with Shooter games without Duck Hunt
df_shooter_no_duckhunt <- df_clean %>%
filter(
Genre == "Shooter",
NA_Sales > 0.00,
Name != "Duck Hunt",
EU_Sales > 0.00
)
# Create subplots
p1 <- ggplot(df_shooter, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Shooter Game Sales")
p2 <- ggplot(df_shooter_no_duckhunt, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Shooter Game Sales no Duck Hunt")
# Plot both plots side by side
grid.arrange(p1, p2, ncol = 2)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

Dies ist ein Vergleich der nordamerikanischen und der europäischen
Verkäufen von Shooter-Spielen. Auf dem linken Plot ist ein ganz
gewaltiger Ausreisser zu sehen, ganz oben links im Bild. Das ist das
Spiel “Duck Hunt”. Wir haben mehr über dieses Spiel recherchiert und
herausgefunden, dass dieses Spiel zum Release des Nintendo Entertainment
System kurz NES herausgekommen ist. Beim Kauf einer NES gab es das Spiel
meistens dazu. Zu dem Zeitpunkt, kam die NES aber erst in Amerika auf
den Markt und noch nicht in Europa. Daher sieht man, dass die Verkäufe
in Amerika fast bei 30 Mio. lag und in europa sogar weniger als 1 Mio.
Einheiten. Im Plot rechts, haben wir diesen Ausreiser entfernt und wir
sehen direkt ein viel genaueres Bild.
# Create linear model
mdl_shooter <- lm(EU_Sales ~ NA_Sales, data = df_shooter)
# Extract model score
mdl_shooter %>%
glance() %>%
pull(r.squared)
[1] 0.4244135
# Create linear model
mdl_shooter_no_duckhunt <- lm(EU_Sales ~ NA_Sales, data = df_shooter_no_duckhunt)
# Extract model score
mdl_shooter_no_duckhunt %>%
glance() %>%
pull(r.squared)
[1] 0.6858556
Wie wir hier anhand der R-Squared sehen, fitted das Modell viel
besser ohne Duck Hunt.
Aber da das Modell auch ohne Duck Hunt nicht sehr gut ist, werden wir
mit diesem Genre keine Vorhersagen durchführen. Diese währen zu ungenau
und wir könnten nicht dahinter stehen.
# Create DataFrame only with Simulation games
df_sim <- df_clean %>%
filter(
Genre == "Simulation",
NA_Sales > 1.00,
EU_Sales > 1.00
)
ggplot(df_sim, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Simulation Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

Auf diesem Plot gibt es wieder einen Punkt, der weit weg von den
anderen ist. Dies ist jedoch in diesem Fall kein Ausreiser, da hier die
Verkäufe von Nordamerika und Europa sehr ähnlich sind. Bei diesem extrem
beliebten Spiel handelt es sich übrigens um den Hundesimulator
“Nintendogs”. ;-)
# Create linear model
mdl_sim <- lm(EU_Sales ~ NA_Sales, data = df_sim)
# Extract model score
mdl_sim %>%
glance() %>%
pull(r.squared)
[1] 0.9233418
# Predict EU Sales for a Simulation Game based on NA Sales
predict_sim <- tibble(NA_Sales = 5)
predict(mdl_sim, predict_sim)
1
5.474846
Wenn ein Simulation Game in NA 5 Million Verkäufe aufweist, liegt die
Vorhersage für EU bei 5.4 Millionen.
# Create DataFrame only with Sport games
df_sport <- df_clean %>%
filter(
Genre == "Sports",
NA_Sales > 1.00,
EU_Sales > 1.00
)
ggplot(df_sport, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Sport Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

Hier haben wir wieder ein Spiel, welches sich von den Massen abhebt,
aber in Amerika sowie in Europa extrem beliebt war. Es handelt sich
natürlich um das Kultspiel “Wii Sports”. Dieses wurde einem auch
meistens beim Kauf einer Nintendo Wii Konsole dazu geschenkt.
# Create linear model
mdl_sport <- lm(EU_Sales ~ NA_Sales, data = df_sport)
# Extract model score
mdl_sport %>%
glance() %>%
pull(r.squared)
[1] 0.9488926
# Predict EU Sales for a Sport Game based on NA Sales
predict_sport <- tibble(NA_Sales = 5)
predict(mdl_sport, predict_sport)
1
4.019965
Wenn ein Sport Game in NA 5 Million Verkäufe aufweist, liegt die
Vorhersage für EU bei 4 Millionen.
Residuenanalyse (zum beurteilen ob das Modell gut ist)
Die Residuen sollten folgende Punkte erfüllen:
- Residuen haben den erwartungswert von 0
- Residuen sind voneinander unabhängig
- Residuen sind normalverteilt
Dies müssen wir nun noch prüfen, um zu bestimmen, ob unsere
Vorhersagen verlässlich sind.
# Create Residual Scatterplot
df <- augment(mdl_racing)
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Racing Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_racing, aes(x = mdl_racing$residuals)) +
geom_histogram(bins = 30) +
geom_density(color = "Red") +
ggtitle("Residuals Model Racing Genre") +
xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100%
normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung
heran, weshalb wir das so akzeptieren können.
# Create Residual Scatterplot
df <- augment(mdl_rp)
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Role-Playing Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_role, aes(x = mdl_rp$residuals)) +
geom_histogram(bins = 30) +
geom_density(color = "Red") +
ggtitle("Residuals Model Role-Playing Genre") +
xlab("residuals")

Auch sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es
kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das
so akzeptieren können.
# Create Residual Scatterplot
df <- augment(mdl_sim)
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Simulation Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_sim, aes(x = mdl_sim$residuals)) +
geom_histogram(bins = 30) +
geom_density(color = "Red") +
ggtitle("Residuals Model Simulation Genre") +
xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100%
normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung
heran, weshalb wir das so akzeptieren können.
# Create Residual Scatterplot
df <- augment(mdl_sport)
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Sport Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_sport, aes(x = mdl_sport$residuals)) +
geom_histogram(bins = 50) +
geom_density(color = "Red") +
ggtitle("Residuals Model Sport Genre") +
xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100%
normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung
heran, weshalb wir das so akzeptieren können.
Wie ähnlich sind sich die Verkaufszahlen von Nordamerika und Europa?
Und wie verhalten sich die japanischen Verkaufszahlen im Vergleich zu
den zwei westlichen Absatzmärkten?
# Color the SPLOM of NA_Sales, EU_Sales, and JP_Sales by nintendo
df_clean %>%
plot_ly(color = ~Genre) %>%
add_trace(
type = 'splom',
dimensions = list(
list(label = 'N. America', values = ~NA_Sales),
list(label = 'Europe', values = ~EU_Sales),
list(label = 'Japan', values = ~JP_Sales)
)
)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Äusserst spannend zu sehen ist, dass sich grosse Unterschiede sehen
lassen, wie sich bestimmte Genre verkauft haben. Die geographische Lage
von dem Absatzmarkt ist dabei eher zweitrangig. Mit Abstand am
häufigsten wurden Action Spiele verkauft.
Verkaufen sich Nintendo Spiele weltweit besser als Spiele Electronic
Arts.
Dafür müssen wir zuerst unser Dataframe Filtern, damit wir nurnoch
Spiele von Nintendo und Electronic Arts haben.
# Create DataFrame with only Nintendo and Sony as Publisher
publishers = c("Nintendo", "Electronic Arts")
df_publisher <- df_clean %>%
filter(
Publisher %in% publishers
)
df_publisher
# Replace publisher name with 0 and 1
df_publisher$Publisher[df_publisher$Publisher == "Electronic Arts"] <- 0
df_publisher$Publisher[df_publisher$Publisher == "Nintendo"] <- 1
# Save as int
df_publisher$Publisher <- as.numeric(df_publisher$Publisher)
# Create logistic model
mdl_publisher <- glm(Publisher ~ NA_Sales, data = df_publisher, family = binomial())
ggplot(df_publisher, aes(x=Global_Sales, y=Publisher)) +
geom_point(alpha=.5, color="Blue") +
stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
labs(
x = "Sales (million)",
y = "1=Nintendo / 0=Electronic Arts",
title = "Probability that a game is from Nintendo based on global sales"
)
`geom_smooth()` using formula 'y ~ x'

Wir können hier gut sehen, dass Nintendo viel erfolgreichere Spiele
produziert hat. Dies deutet auch an, dass Nintendo beliebter ist als
Electronic Arts.
Verkaufen sich in Japan Nintendo Wii Spiele besser als Nintendo DS
Spiele?
Nun filtern wir zuerst unseren Dataframe nach Wii und nach DS
Spielen.
# Create DataFrame with only Wii and DS as Platforms
platforms = c("Wii", "DS")
df_platform <- df_clean %>%
filter(
Platform %in% platforms
)
df_platform
# Replace platform name with 0 and 1
df_platform$Platform[df_platform$Platform == "Wii"] <- 0
df_platform$Platform[df_platform$Platform == "DS"] <- 1
# Save as int
df_platform$Platform <- as.numeric(df_platform$Platform)
# Create logistic model
mdl_platform <- glm(Platform ~ JP_Sales, data = df_platform, family = binomial())
ggplot(df_platform, aes(x=JP_Sales, y=Platform)) +
geom_point(alpha=.2, color="Blue") +
stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
labs(
x = "Sales (million)",
y = "1=Wii / 0=DS",
title = "Probability that a game is from Wii based on sales in Japan"
)
`geom_smooth()` using formula 'y ~ x'

Anhand von diesem Plot kann man erkennen, dass ein Spiel, welches
sich über 2 Mio. mal verkauft hat, eher über die Platform Wii verkauft
wurde. Daraus könnte man deuten, dass die Nintendo Wii in Japan
beliebter ist als der Nintendo DS.
Fazit
Bei den meisten Genren ist es nicht möglich die Verkäufe in Europa
anhand der Verkäufe in Nord Amerika vorherzusagen. Wir haben jedoch
einige Genren gefunden, bei denen Vorhersagen möglich ist:
- Racing / Rennspiele
- Role-Playing / Rollenspiele
- Simulation / Simulationsspiele
- Sport / Sportspiele
Sehr interessant zu sehen war das Genre Simulation. Unser Modell,
welches bei 92% Genauigkeit liegt sagt voraus, dass ein beliebiges Spiel
in Europa besser verkauft wird als in Nord Amerika. Bei allen anderen
Genren verkaufen sich die Spiele in Nord Amerika besser.
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBtb2RlbHMgd2l0aCBSIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpEYXMgWmllbCBpc3QgZXMsIGF1cyBkZW0gRGF0YWNhbXAgRGF0ZW5zYXR6IFtWaWRlbyBHYW1lIFNhbGVzIERhdGVdKGh0dHBzOi8vYXBwLmRhdGFjYW1wLmNvbS93b3Jrc3BhY2UvZGF0YXNldHMvZGF0YXNldC1weXRob24tdmlkZW8tZ2FtZXMtc2FsZXMpIGZvbGdlbmRlIEZyYWdlc3RlbGx1bmcgLyBIeXBvdGhlc2UgenUgYmVhbnR3b3J0ZW46CgoKIyMjIEZyYWdlc3RlbGx1bmc6ICJJc3QgZXMgd2FocnNjaGVpbmxpY2hlciwgZGFzcyBzaWNoIGJlc3RpbW10ZSBTcGllbGdlbnJlcyBpbiBFdXJvcGEgc2lnbmlmaWthbnQgKFVudGVyc2NoaWVkIHZvbiA1MCUpIGJlc3NlciB2ZXJrYXVmZW4gbGFzc2VuIGFscyBpbSBKYXBhbmlzY2hlbiB1bmQgTm9yZGFtZXJpa2FuaXNjaGVuIE1hcmt0PyIKCgpBbHMgRWluZsO8aHJ1bmcgd2VyZGVuIHdpciBhdWYgRGF0YWNhbXAgZm9sZ2VuZGUgS3Vyc2UgZHVyY2hnZWhlbjoKCi0gW0ludHJvZHVjdGlvbiB0byBSZWdyZXNzaW9uIGluIFJdKGh0dHBzOi8vYXBwLmRhdGFjYW1wLmNvbS9sZWFybi9jb3Vyc2VzL2ludHJvZHVjdGlvbi10by1yZWdyZXNzaW9uLWluLXIpCgotIFtJbnRlcm1lZGlhdGUgUmVncmVzc2lvbiBpbiBSXShodHRwczovL2FwcC5kYXRhY2FtcC5jb20vbGVhcm4vY291cnNlcy9pbnRlcm1lZGlhdGUtcmVncmVzc2lvbi1pbi1yKQoKCmBgYHtyfQojIEltcG9ydCBsaWJyYXJpZXMKbGlicmFyeSgicGxvdGx5IikKbGlicmFyeSgiZ2dwbG90MiIpCmxpYnJhcnkoInBseXIiKQpsaWJyYXJ5KCJkcGx5ciIpCmxpYnJhcnkoImJyb29tIikKbGlicmFyeSgiZ3JpZEV4dHJhIikgICAKYGBgCgpgYGB7cn0KIyBSZWFkIGNzdiBmcm9tIGZvbGRlciAiZGF0YSIKZGYgPSByZWFkLmNzdigiLi9kYXRhL3ZpZGVvX2dhbWVzX2RhdGEuY3N2IikKCmhlYWQoZGYsIDEwKQpgYGAKIyMjIERhdGEgV3JhbmdsaW5nCkJldm9yIHdpciBtaXQgZGVuIFZpc3VhbGlzaWVydW5nZW4gdW5kIGRlbSBFcnN0ZWxsZW4gZGVyIE1vZGVsbGUgYmVnaW5uZW4ga8O2bm5lbiwgbcO8c3NlbiB3aXIgZGllIERhdGVuIHPDpHViZXJuLiBEYXMgaGVpc3N0IGVzIHNvbGx0ZSBrZWluZSBEdXBsaWthdGUgZ2ViZW4sIGZlaGxlbmRlIFdlcnRlIHNvbGx0ZW4ga29ycmVrdCBlaW5nZXRyYWdlbiB3ZXJkZW4gdW5kIERhdGVuLCBkaWUgbmljaHQgdmVyd2VuZGV0IHdlcmRlbiwgc29sbHRlbiBnZWzDtnNjaHQgd2VyZGVuLgoKRXMgaGF0ICJOL0EiIFdlcnRlIGluIGRlbiBTcGFsdGVuICJZZWFyIiB1bmQgIlB1Ymxpc2hlciIuIERpZXNlIFdlcnRlIHNvbGx0ZW4ga29ycmVrdGUgIk5BIiBXZXJ0ZSBzZWluLCBkYW1pdCBzaWUgYmVpIGRlbiBWaXN1YWxpc2llcnVuZ2VuIHVuZCBCZXJlY2hudW5nZW4gbmljaHQgYmVyw7xja3NpY2h0aWd0IHdlcmRlbi4KCmBgYHtyfQojIFNob3cgcm93cyB3aXRoICJOL0EiIHZhbHVlcwpkZltncmVwKCJOL0EiLCBkZiRQdWJsaXNoZXIpLF0KZGZbZ3JlcCgiTi9BIiwgZGYkWWVhciksXQpgYGAKCmBgYHtyfQojIFJlcGxhY2UgIk4vQSIgd2l0aCAiTkEiCmRmW2RmID09ICJOL0EiXSA8LSBOQQoKZGYgPC0gZGYgJT4lCiAgZmlsdGVyKGRmJEdsb2JhbF9TYWxlcyA+IDAuMSkKCmRmCmBgYAoKYGBge3J9CiMgQ2hlY2sgaWYgdmFsdWVzIGhhdmUgYmVlbiBjb252ZXJ0ZWQKZGYgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+c3VtKGlzLm5hKC4pKSkpCmBgYApEYSAyMDE3IG51ciAzIEVpbnRyw6RnZSB1bmQgMjAyMCBudXIgMSBFaW50cmFnIGJlaW5oYWx0ZXQsIHdlcmRlbiB3aXIgZGllc2UgSmFocmVuIG5pY2h0IGJlcsO8Y2tzaWNodGlnZW4gdW5kIGF1cyBkZW0gRGF0YWZyYW1lIGzDtnNjaGVuLiBEYSBkaWVzZSBuaWNodCB2b2xsc3TDpG5kaWcgc2luZCwga8O2bm50ZW4gdW5zZXJlIE1vZGVsbGUgdW5nZW5hdSB3ZXJkZW4uCgpgYGB7cn0KIyBSZW1vdmUgeWVhcnMgMjAxNyBhbmQgMjAyMCBmcm9tIGRhdGFzZXQKZGZfY2xlYW4gPC0gZGZbIShkZiRZZWFyID09ICIyMDE3IiB8IGRmJFllYXIgPT0gIjIwMjAiKSxdCgojIFJlbW92ZSB1bm5lY2Vzc2FyeSBjb2x1bW5zIGJlY2F1c2UgdGhleSBhcmUgbm90IG5lZWRlZCBmb3Igb3VyIHRoZXNpcwpkcm9wIDwtIGMoIlJhbmsiKQpkZl9jbGVhbiA8LSBkZl9jbGVhblssIShuYW1lcyhkZl9jbGVhbikgJWluJSBkcm9wKV0KCmRmX2NsZWFuCmBgYAoKYGBge3J9CiMgU2V0IGRhdGEgdG8gY29ycmVjdCB0eXBlCmRmX2NsZWFuJEdlbnJlIDwtIGFzLmZhY3RvcihkZl9jbGVhbiRHZW5yZSkKZGZfY2xlYW4kWWVhciA8LSBhcy5udW1lcmljKGRmX2NsZWFuJFllYXIpCmBgYAoKIyMjIEVyc3RlIFBsb3RzIGVyc3RlbGxlbgoKV2lyIGVyc3RlbGxlbiBlaW4gcGFhciBlcnN0ZSBQbG90cyB1bSB1bnMgZWluZW4gw5xiZXJibGljayDDvGJlciBkaWUgRGF0ZW4genUgdmVyc2NoYWZmZW4uCgpgYGB7cn0KbmEgPC0gc3VtKGRmX2NsZWFuWywgJ05BX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkKZXUgPC0gc3VtKGRmX2NsZWFuWywgJ0VVX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkKanAgPC0gc3VtKGRmX2NsZWFuWywgJ0pQX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkKbyA8LSBzdW0oZGZfY2xlYW5bLCAnT3RoZXJfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQpnIDwtIHN1bShkZl9jbGVhblssICdHbG9iYWxfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQoKZmlnIDwtIHBsb3RfbHkoCiAgeSA9IGMobmEsIGV1LCBqcCwgbyksIAogIHggPSBjKCJOb3J0aCBBbWVyaWNhIiwgIkV1cm9wZSIsICJKYXBhbiIsICJPdGhlciIpLCAKICB0eXBlID0gJ2JhcicsCiAgd2lkdGggPSA4MDAKKQoKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIlZpZGVvIEdhbWUgU2FsZXMgT3ZlcnZpZXciLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVnaW9uIiksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJTYWxlcyAobWlsbGlvbikiKSkKCmZpZwpgYGAKQW5oYW5kIGRpZXNlcyBEaWFncmFtbWVzLCBrYW5uIG1hbiBzZWhlbiwgZGFzcyBOb3JkYW1lcmlrYSBtaXQgQWJzdGFuZCBkZXIgZ3LDtnNzdGUgQWJzYXR6bWFya3QgaW0gR2FtZS1CZXJlaWNoIGlzdC4gCgoKTnVuIHdvbGxlbiB3aXIgbm9jaCBkaWUgVmVydGVpbHVuZyBkZXIgdmVyc2NoaWVkZW5lbiBTcGllbGdlbnJlcyBiZXNzZXIgc2VoZW46CgpgYGB7cn0KIyBHcm91cCBieSBnZW5yZSBhbmQgc3VtbWFyaXplIGdhbWUgc2FsZXMgdG8gZWFjaCByZWdpb24KZGZfZ2VucmUgPC0gZGZfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoR2VucmUpICU+JQogIHN1bW1hcml6ZSgKICAgIE5BX1NhbGVzX1N1bSA9IHN1bShOQV9TYWxlcyksCiAgICBFVV9TYWxlc19TdW0gPSBzdW0oRVVfU2FsZXMpLCAKICAgIEpQX1NhbGVzX1N1bSA9IHN1bShKUF9TYWxlcyksCiAgICBPdGhlcl9TYWxlc19TdW0gPSBzdW0oT3RoZXJfU2FsZXMpLAogICAgR2xvYmFsX1NhbGVzX1N1bSA9IHN1bShHbG9iYWxfU2FsZXMpCiAgKQoKIyBQbG90IGdyb3VwZWQgYmFyIGNoYXJ0IHZpZGVvIGdhbWUgc2FsZXMgYnkgZ2VucmUKZmlnIDwtIHBsb3RfbHkoCiAgZGZfZ2VucmUsIHkgPSB+R2VucmUsIHggPSB+TkFfU2FsZXNfU3VtLCB0eXBlID0gImJhciIsIG5hbWUgPSAiTm9ydGggQW1lcmljYSIsIHdpZHRoID0gMTAwMCwgaGVpZ2h0ID0gODAwKSAlPiUgCiAgYWRkX3RyYWNlKHggPSB+RVVfU2FsZXNfU3VtLCBuYW1lID0gIkV1cm9wZSIpICU+JQogIGFkZF90cmFjZSh4ID0gfkpQX1NhbGVzX1N1bSwgbmFtZSA9ICJKYXBhbiIpICU+JQogIGFkZF90cmFjZSh4ID0gfk90aGVyX1NhbGVzX1N1bSwgbmFtZSA9ICJPdGhlciIpICU+JQogIGxheW91dCgKICAgIHRpdGxlID0gIlZpZGVvIEdhbWUgU2FsZXMgYnkgR2VucmUiLAogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpLAogICAgYmFybW9kZSA9ICJncm91cCIKICApCgpmaWcKYGBgCkhpZXIgc2llaHQgbWFuIGd1dCwgZGFzcyBKYXBhbiBpbSBWZXJnbGVpY2ggenUgZGVuIGFuZGVyZW4gTcOkcmt0ZW4gdmllbCBtZWhyIFJvbGUtUGxheSB1bmQgU3RyYXRlZ2llIFNwaWVsZSB2ZXJrYXVmdC4gRGFmw7xyIHdlcmRlbiBpbiBKYXBhbiBhYmVyIHZpZWwgd2VuaWdlciBTaG9vdGVyIHVuZCBBY3Rpb24gU3BpZWxlIGFscyBkZW4gYW5kZXJlbiBSZWdpb25lbiB2ZXJrYXVmdC4gCgpgYGB7cn0KZGZfeWVhciA8LSBkZl9jbGVhbiAlPiUKICBncm91cF9ieShZZWFyKSAlPiUKICBzdW1tYXJpemUoCiAgICBOQV9TYWxlc19TdW0gPSBzdW0oTkFfU2FsZXMpLAogICAgRVVfU2FsZXNfU3VtID0gc3VtKEVVX1NhbGVzKSwgCiAgICBKUF9TYWxlc19TdW0gPSBzdW0oSlBfU2FsZXMpLAogICAgT3RoZXJfU2FsZXNfU3VtID0gc3VtKE90aGVyX1NhbGVzKSwKICAgIEdsb2JhbF9TYWxlc19TdW0gPSBzdW0oR2xvYmFsX1NhbGVzKQogICkKCmZpZyA8LSBwbG90X2x5KAogIGRmX3llYXIsIHkgPSB+TkFfU2FsZXNfU3VtLCB4ID0gflllYXIsIHR5cGUgPSAiYmFyIiwgbmFtZSA9ICJOb3J0aCBBbWVyaWNhIiwgd2lkdGggPSA5MDAsIGhlaWdodCA9IDUwMCkgJT4lIAogIGFkZF90cmFjZSh5ID0gfkVVX1NhbGVzX1N1bSwgbmFtZSA9ICJFdXJvcGUiKSAlPiUKICBhZGRfdHJhY2UoeSA9IH5KUF9TYWxlc19TdW0sIG5hbWUgPSAiSmFwYW4iKSAlPiUKICBhZGRfdHJhY2UoeSA9IH5PdGhlcl9TYWxlc19TdW0sIG5hbWUgPSAiT3RoZXIiKSAlPiUKICBsYXlvdXQoCiAgICB0aXRsZSA9ICJWaWRlbyBHYW1lIGZyb20gU2FsZXMgYnkgWWVhciIsCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiWWVhciIpLAogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpLAogICAgYmFybW9kZSA9ICJzdGFjayIKICApCgpmaWcKYGBgCsOcYmVycmFzY2hlbmRlcndlaXNlLCBzaWVodCBtYW4gaW4gZGllc2VtIERpYWdyYW1tLCBkYXNzIGRpZSBWaWRlb3NwaWVsIFZlcmvDpHVmZSB6d2lzY2hlbiBkZW4gSmFocmVuIDIwMDUgdW5kIDIwMTIgZ2Vib29tdCBoYWJlbi4gRGFuYWNoIGlzdCBkaWUgS3VydmUgd2llZGVyIGFiZ2VmbGFjaHQuIERpZXMga8O2bm50ZSB1bnRlciBhbmRlcmVtIGRhcmFuIGxpZWdlbiwgZGFzcyBpbiBkaWVzZXIgWmVpdCB2aWVsZSBuZXVlIEtvbnNvbGVuIGF1ZiBkZW4gTWFya3QgZ2Vrb21tZW4gc2luZCwgd2VsY2hlIGdyb3NzZSBOZXVlcnVuZ2VuIG1pdCBzaWNoIGJyYWNodGVuLiBEaWUgUGxheXN0YXRpb24gMywgZGllIFhib3ggMzYwIHVuZCBkaWUgTmludGVuZG8gV2lpIHNpbmQgYWxsZSBpbiAyMDA1LTIwMDYgYXVmIGRlbiBNYXJrdCBnZWtvbW1lbiB1bmQgaGFiZW4gZGVuIFNwaWVsZW1hcmt0IGFuc2NoZWluZW5kIHN0YXJrIGJlZWluZmx1c3N0LiAKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBvZiBzYWxlcyBvZiBlYWNoIGdlbnJlIGZyb20gZWFjaCByZWdpb24KZGZfc2FsZXNfYXZnIDwtIGRmX2NsZWFuICU+JQogIGdyb3VwX2J5KEdlbnJlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBFVV9TYWxlc19BdmcgPSBtZWFuKEVVX1NhbGVzKSwKICAgIE5BX1NhbGVzX0F2ZyA9IG1lYW4oTkFfU2FsZXMpLAogICAgSlBfU2FsZXNfQXZnID0gbWVhbihKUF9TYWxlcyksCiAgICBPdGhlcl9TYWxlc19BdmcgPSBtZWFuKE90aGVyX1NhbGVzKSwKICAgIEdsb2JhbF9TYWxlc19BdmcgPSBtZWFuKEdsb2JhbF9TYWxlcykpCgpkZl9zYWxlc19hdmcKYGBgCkhpZXIgc2VoZW4gd2lyIGRpZSBkdXJjaHNjaG5pdHRsaWNoZSBBbnpoYWwgdm9uIFZlcmvDpHVmZSBwcm8gR2VucmUuCgojIyBSZWdyZXNzaW9uc21vZGVsbGUKCkRhIHdpciB1bnNlcmUgRGF0ZW4gamV0enQgYmVzc2VyIHZlcnN0ZWhlbiwga8O2bm5lbiB3aXIgbWl0IGRlbiBSZWdyZXNzaW9uc21vZGVsbGVuIHVuZCBtaXQgZGVyIEJlYW50d29ydHVuZyB1bnNlcmVyIEZyYWdlc3RlbGx1bmcgYmVnaW5uZW4uIEFsbGVyZGluZ3MgbXVzc3RlbiB3aXIgZmVzdHN0ZWxsZW4sIGRhc3MgdW5zZXJlIEZyYWdlc3RlbGx1bmcgdW5nZXNjaGlja3Qgd2FyIHVuZCB3aXIgZGllc2UgbmljaHQgYXVzcmVpY2hlbmQgYmVhbnR3b3J0ZW4ga8O2bm5lbi4gRGFoZXIgaGFiZW4gd2lyIHVucyBmb2xnZW5kZSBBbHRlcm5hdGl2LUZyYWdlc3RlbGx1bmcgw7xiZXJsZWd0OgoKIyMjIEthbm4gbWFuIGFuaGFuZCBkZXIgbm9yZGFtZXJpa2FuaXNjaGVuIFZlcmvDpHVmZSB2b3JhdXNzYWdlbiwgd2llIHNpY2ggZWluIEdlbnJlIGltIGV1cm9ww6Rpc2NoZW4gTWFya3QgdmVya2F1ZmVuIHdpcmQ/CgpEaWVzZSBGcmFnZSBrw7ZubnRlIGVpbmVyIEZpcm1hIGRhYmVpIGhlbGZlbiB6dSBlbnRzY2hlaWRlbiwgd2lldmllbCBXZXJiZWJ1ZGdldCBkaWVzZSBpbiBFdXJvcGEgYXVzZ2ViZW4gc29sbCwgbmFjaGRlbSBlaW4gU3BpZWwgaW4gQW1lcmlrYSBiZXJlaXRzIGF1ZiBkZW4gTWFya3QgZ2Vrb21tZW4gaXN0LiAKCiMjIyBEYXRlbiBvcHRpbWllcmVuCgpJbiB1bnNlcmVtIERhdGVuc2F0eiBnaWJ0IGVzIGVpbmlnZSBBdXNyZWlzc2VyLiBadW0gQmVpc3BpZWwgZ2lidCBlcyBTcGllbGUsIHdlbGNoZSBudXIgaW4gZWluemVsbmVuIFJlZ2lvbmVuIGF1ZiBkZW4gTWFya3QgZ2Vrb21tZW4gc2luZCB1bmQgZGllIGluIGFuZGVyZW4gTMOkbmRlcm4gZ2FybmljaHQgdmVya2F1ZnQgd3VyZGVuLiBEaWVzZSBUYXRzYWNoZSB3w7xyZGUgYmVpbSBFcnN0ZWxsZW4gZGVyIE1vZGVsbGUgenUgZ3Jvc3NlbiBBYndlaWNodW5nZW4gdW5kIFVuZ2VuYXVpZ2tlaXRlbiBmw7xocmVuLiBEYWhlciBiZXLDvGNrc2ljaHRpZ2VuIHdpciBudXIgZGllIEdhbWVzLCB3ZWxjaGUgw7xiZXIgMSBNaW8uIFZlcmvDpHVmZSBoYWJlbi4KClVudGVuIGhhYmVuIHdpciBkaWUgR2VucmVzIG1pdCBkZW4gYmVzdGVuIEZpdHMgZ2Vub21tZW4sIGRhIGJlaSBkZW4gYW5kZXJlbiBTcGllbGdlbnJlcyBkZXIgci1zcXVhcmVkIFdlcnQgdW50ZXIgNTAlIGxhZy4gRGFzIGJlZGV1dGV0LCBkYXNzIGVzIGRvcnQga2VpbmVuIFp1c2FtbWVuaGFuZyBnaWJ0LCB3ZXNoYWxiIHdpciBkaWVzZSBlYmVuZmFsbHMgbmljaHQgYmVhY2h0ZW4gd2VyZGVuLgoKCldpciBlcnN0ZWxsZW4gZGFzIERhdGFmcmFtZSBtaXQgYWxsZW4gUmFjaW5nIEdhbWVzOgpgYGB7cn0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBSYWNpbmcgZ2FtZXMKZGZfcmFjaW5nIDwtIGRmX2NsZWFuICU+JQogIGZpbHRlcigKICAgIEdlbnJlID09ICJSYWNpbmciLAogICAgTkFfU2FsZXMgPiAxLjAwLAogICAgRVVfU2FsZXMgPiAxLjAwCiAgKQoKI2NyZWF0ZSBzY2F0dGVycGxvdApnZ3Bsb3QoZGZfcmFjaW5nLCBhZXMoeD1FVV9TYWxlcywgeT1OQV9TYWxlcykpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsKICBsYWJzKHRpdGxlID0gIlJhY2luZyBHYW1lIFNhbGVzIE92ZXJ2aWV3IikKYGBgIApgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsCm1kbF9yYWNpbmcgPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3JhY2luZykKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfcmFjaW5nICU+JQogIGdsYW5jZSgpICU+JQogIHB1bGwoci5zcXVhcmVkKQoKIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFJhY2luZyBHYW1lIGJhc2VkIG9uIE5BIFNhbGVzCnByZWRpY3RfcmFjaW5nIDwtIHRpYmJsZShOQV9TYWxlcyA9IDUpCnByZWRpY3QobWRsX3JhY2luZywgcHJlZGljdF9yYWNpbmcpCmBgYApXZW5uIGVpbiBSYWNpbmcgR2FtZSBpbiBOb3JkIEFtZXJpa2EgNSBNaWxsaW9uZW4gVmVya8OkdWZlIGF1ZndlaXN0LCBsaWVndCBkaWUgVmVya2F1ZnMtVm9yaGVyc2FnZSBmw7xyIEV1cm9wYSBiZWkgcnVuZCAzLjkgTWlsbGlvbmVuLiBJbiBBbWVyaWthIHNpbmQgZGllc2UgU3BpZWxlIGFsc28gYmVsaWVidGVyIGFscyBiZWkgdW5zIGluIEV1cm9wYS4KCmBgYHtyfQojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFJvbGUtUGxheWluZyBnYW1lcwpkZl9yb2xlIDwtIGRmX2NsZWFuICU+JQogIGZpbHRlcigKICAgIEdlbnJlID09ICJSb2xlLVBsYXlpbmciLAogICAgTkFfU2FsZXMgPiAxLjAwLAogICAgRVVfU2FsZXMgPiAxLjAwCiAgKQoKI2NyZWF0ZSBzY2F0dGVycGxvdApnZ3Bsb3QoZGZfcm9sZSwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJSb2xlLVBsYXlpbmcgR2FtZSBTYWxlcyBPdmVydmlldyIpCmBgYApgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsCm1kbF9ycCA8LSBsbShFVV9TYWxlcyB+IE5BX1NhbGVzLCBkYXRhID0gZGZfcm9sZSkKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfcnAgJT4lCiAgZ2xhbmNlKCkgJT4lCiAgcHVsbChyLnNxdWFyZWQpCgojIFByZWRpY3QgRVUgU2FsZXMgZm9yIGEgUm9sZS1QbGF5aW5nIEdhbWUgYmFzZWQgb24gTkEgU2FsZXMKcHJlZGljdF9ycCA8LSB0aWJibGUoTkFfU2FsZXMgPSA1KQpwcmVkaWN0KG1kbF9ycCwgcHJlZGljdF9ycCkKYGBgCmBgYHtyfQojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFNob290ZXIgZ2FtZXMKZGZfc2hvb3RlciA8LSBkZl9jbGVhbiAlPiUKICBmaWx0ZXIoCiAgICBHZW5yZSA9PSAiU2hvb3RlciIsCiAgICBOQV9TYWxlcyA+IDAuMDAsCiAgICBFVV9TYWxlcyA+IDAuMDAKICApCgojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFNob290ZXIgZ2FtZXMgd2l0aG91dCBEdWNrIEh1bnQKZGZfc2hvb3Rlcl9ub19kdWNraHVudCA8LSBkZl9jbGVhbiAlPiUKICBmaWx0ZXIoCiAgICBHZW5yZSA9PSAiU2hvb3RlciIsCiAgICBOQV9TYWxlcyA+IDAuMDAsCiAgICBOYW1lICE9ICJEdWNrIEh1bnQiLAogICAgRVVfU2FsZXMgPiAwLjAwCiAgKQoKIyBDcmVhdGUgc3VicGxvdHMKcDEgPC0gZ2dwbG90KGRmX3Nob290ZXIsIGFlcyh4PUVVX1NhbGVzLCB5PU5BX1NhbGVzKSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgKwogIGxhYnModGl0bGUgPSAiU2hvb3RlciBHYW1lIFNhbGVzIikKCnAyIDwtIGdncGxvdChkZl9zaG9vdGVyX25vX2R1Y2todW50LCBhZXMoeD1FVV9TYWxlcywgeT1OQV9TYWxlcykpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsKICBsYWJzKHRpdGxlID0gIlNob290ZXIgR2FtZSBTYWxlcyBubyBEdWNrIEh1bnQiKQoKIyBQbG90IGJvdGggcGxvdHMgc2lkZSBieSBzaWRlCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAyKSAgCmBgYApEaWVzIGlzdCBlaW4gVmVyZ2xlaWNoIGRlciBub3JkYW1lcmlrYW5pc2NoZW4gdW5kIGRlciBldXJvcMOkaXNjaGVuIFZlcmvDpHVmZW4gdm9uIFNob290ZXItU3BpZWxlbi4gQXVmIGRlbSBsaW5rZW4gUGxvdCBpc3QgZWluIGdhbnogZ2V3YWx0aWdlciBBdXNyZWlzc2VyIHp1IHNlaGVuLCBnYW56IG9iZW4gbGlua3MgaW0gQmlsZC4gRGFzIGlzdCBkYXMgU3BpZWwgIkR1Y2sgSHVudCIuIFdpciBoYWJlbiBtZWhyIMO8YmVyIGRpZXNlcyBTcGllbCByZWNoZXJjaGllcnQgdW5kIGhlcmF1c2dlZnVuZGVuLCBkYXNzIGRpZXNlcyBTcGllbCB6dW0gUmVsZWFzZSBkZXMgTmludGVuZG8gRW50ZXJ0YWlubWVudCBTeXN0ZW0ga3VyeiBORVMgaGVyYXVzZ2Vrb21tZW4gaXN0LiBCZWltIEthdWYgZWluZXIgTkVTIGdhYiBlcyBkYXMgU3BpZWwgbWVpc3RlbnMgZGF6dS4gWnUgZGVtIFplaXRwdW5rdCwga2FtIGRpZSBORVMgYWJlciBlcnN0IGluIEFtZXJpa2EgYXVmIGRlbiBNYXJrdCB1bmQgbm9jaCBuaWNodCBpbiBFdXJvcGEuIERhaGVyIHNpZWh0IG1hbiwgZGFzcyBkaWUgVmVya8OkdWZlIGluIEFtZXJpa2EgZmFzdCBiZWkgMzAgTWlvLiBsYWcgdW5kIGluIGV1cm9wYSBzb2dhciB3ZW5pZ2VyIGFscyAxIE1pby4gRWluaGVpdGVuLiBJbSBQbG90IHJlY2h0cywgaGFiZW4gd2lyIGRpZXNlbiBBdXNyZWlzZXIgZW50ZmVybnQgdW5kIHdpciBzZWhlbiBkaXJla3QgZWluIHZpZWwgZ2VuYXVlcmVzIEJpbGQuIAoKYGBge3J9CiMgQ3JlYXRlIGxpbmVhciBtb2RlbAptZGxfc2hvb3RlciA8LSBsbShFVV9TYWxlcyB+IE5BX1NhbGVzLCBkYXRhID0gZGZfc2hvb3RlcikKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfc2hvb3RlciAlPiUKICBnbGFuY2UoKSAlPiUKICBwdWxsKHIuc3F1YXJlZCkKYGBgCmBgYHtyfQojIENyZWF0ZSBsaW5lYXIgbW9kZWwKbWRsX3Nob290ZXJfbm9fZHVja2h1bnQgPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3Nob290ZXJfbm9fZHVja2h1bnQpCgojIEV4dHJhY3QgbW9kZWwgc2NvcmUKbWRsX3Nob290ZXJfbm9fZHVja2h1bnQgJT4lCiAgZ2xhbmNlKCkgJT4lCiAgcHVsbChyLnNxdWFyZWQpCmBgYApXaWUgd2lyIGhpZXIgYW5oYW5kIGRlciBSLVNxdWFyZWQgc2VoZW4sIGZpdHRlZCBkYXMgTW9kZWxsIHZpZWwgYmVzc2VyIG9obmUgRHVjayBIdW50LiAKCkFiZXIgZGEgZGFzIE1vZGVsbCBhdWNoIG9obmUgRHVjayBIdW50IG5pY2h0IHNlaHIgZ3V0IGlzdCwgd2VyZGVuIHdpciBtaXQgZGllc2VtIEdlbnJlIGtlaW5lIFZvcmhlcnNhZ2VuIGR1cmNoZsO8aHJlbi4gRGllc2Ugd8OkaHJlbiB6dSB1bmdlbmF1IHVuZCB3aXIga8O2bm50ZW4gbmljaHQgZGFoaW50ZXIgc3RlaGVuLgoKYGBge3J9CiMgQ3JlYXRlIERhdGFGcmFtZSBvbmx5IHdpdGggU2ltdWxhdGlvbiBnYW1lcwpkZl9zaW0gPC0gZGZfY2xlYW4gJT4lCiAgZmlsdGVyKAogICAgR2VucmUgPT0gIlNpbXVsYXRpb24iLAogICAgTkFfU2FsZXMgPiAxLjAwLAogICAgRVVfU2FsZXMgPiAxLjAwCikKCmdncGxvdChkZl9zaW0sIGFlcyh4PUVVX1NhbGVzLCB5PU5BX1NhbGVzKSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgKwogIGxhYnModGl0bGUgPSAiU2ltdWxhdGlvbiBHYW1lIFNhbGVzIE92ZXJ2aWV3IikKYGBgCkF1ZiBkaWVzZW0gUGxvdCBnaWJ0IGVzIHdpZWRlciBlaW5lbiBQdW5rdCwgZGVyIHdlaXQgd2VnIHZvbiBkZW4gYW5kZXJlbiBpc3QuIERpZXMgaXN0IGplZG9jaCBpbiBkaWVzZW0gRmFsbCBrZWluIEF1c3JlaXNlciwgZGEgaGllciBkaWUgVmVya8OkdWZlIHZvbiBOb3JkYW1lcmlrYSB1bmQgRXVyb3BhIHNlaHIgw6RobmxpY2ggc2luZC4gQmVpIGRpZXNlbSBleHRyZW0gYmVsaWVidGVuIFNwaWVsIGhhbmRlbHQgZXMgc2ljaCDDvGJyaWdlbnMgdW0gZGVuIEh1bmRlc2ltdWxhdG9yICJOaW50ZW5kb2dzIi4gOy0pCgpgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsCm1kbF9zaW0gPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3NpbSkKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfc2ltICU+JQogIGdsYW5jZSgpICU+JQogIHB1bGwoci5zcXVhcmVkKQoKIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFNpbXVsYXRpb24gR2FtZSBiYXNlZCBvbiBOQSBTYWxlcwpwcmVkaWN0X3NpbSA8LSB0aWJibGUoTkFfU2FsZXMgPSA1KQpwcmVkaWN0KG1kbF9zaW0sIHByZWRpY3Rfc2ltKQpgYGAKV2VubiBlaW4gU2ltdWxhdGlvbiBHYW1lIGluIE5BIDUgTWlsbGlvbiBWZXJrw6R1ZmUgYXVmd2Vpc3QsIGxpZWd0IGRpZSBWb3JoZXJzYWdlIGbDvHIgRVUgYmVpIDUuNCBNaWxsaW9uZW4uCgpgYGB7cn0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBTcG9ydCBnYW1lcwpkZl9zcG9ydCA8LSBkZl9jbGVhbiAlPiUKICBmaWx0ZXIoCiAgICBHZW5yZSA9PSAiU3BvcnRzIiwKICAgIE5BX1NhbGVzID4gMS4wMCwKICAgIEVVX1NhbGVzID4gMS4wMAogICkKCmdncGxvdChkZl9zcG9ydCwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJTcG9ydCBHYW1lIFNhbGVzIE92ZXJ2aWV3IikKYGBgCkhpZXIgaGFiZW4gd2lyIHdpZWRlciBlaW4gU3BpZWwsIHdlbGNoZXMgc2ljaCB2b24gZGVuIE1hc3NlbiBhYmhlYnQsIGFiZXIgaW4gQW1lcmlrYSBzb3dpZSBpbiBFdXJvcGEgZXh0cmVtIGJlbGllYnQgd2FyLiBFcyBoYW5kZWx0IHNpY2ggbmF0w7xybGljaCB1bSBkYXMgS3VsdHNwaWVsICJXaWkgU3BvcnRzIi4gRGllc2VzIHd1cmRlIGVpbmVtIGF1Y2ggbWVpc3RlbnMgYmVpbSBLYXVmIGVpbmVyIE5pbnRlbmRvIFdpaSBLb25zb2xlIGRhenUgZ2VzY2hlbmt0LiAKCmBgYHtyfQojIENyZWF0ZSBsaW5lYXIgbW9kZWwKbWRsX3Nwb3J0IDwtIGxtKEVVX1NhbGVzIH4gTkFfU2FsZXMsIGRhdGEgPSBkZl9zcG9ydCkKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfc3BvcnQgJT4lCiAgZ2xhbmNlKCkgJT4lCiAgcHVsbChyLnNxdWFyZWQpCgojIFByZWRpY3QgRVUgU2FsZXMgZm9yIGEgU3BvcnQgR2FtZSBiYXNlZCBvbiBOQSBTYWxlcwpwcmVkaWN0X3Nwb3J0IDwtIHRpYmJsZShOQV9TYWxlcyA9IDUpCnByZWRpY3QobWRsX3Nwb3J0LCBwcmVkaWN0X3Nwb3J0KQpgYGAKV2VubiBlaW4gU3BvcnQgR2FtZSBpbiBOQSA1IE1pbGxpb24gVmVya8OkdWZlIGF1ZndlaXN0LCBsaWVndCBkaWUgVm9yaGVyc2FnZSBmw7xyIEVVIGJlaSA0IE1pbGxpb25lbi4KCgojIyMgUmVzaWR1ZW5hbmFseXNlICh6dW0gYmV1cnRlaWxlbiBvYiBkYXMgTW9kZWxsIGd1dCBpc3QpCkRpZSBSZXNpZHVlbiBzb2xsdGVuIGZvbGdlbmRlIFB1bmt0ZSBlcmbDvGxsZW46CgotIFJlc2lkdWVuIGhhYmVuIGRlbiBlcndhcnR1bmdzd2VydCB2b24gMAotIFJlc2lkdWVuIHNpbmQgdm9uZWluYW5kZXIgdW5hYmjDpG5naWcKLSBSZXNpZHVlbiBzaW5kIG5vcm1hbHZlcnRlaWx0CgpEaWVzIG3DvHNzZW4gd2lyIG51biBub2NoIHByw7xmZW4sIHVtIHp1IGJlc3RpbW1lbiwgb2IgdW5zZXJlIFZvcmhlcnNhZ2VuIHZlcmzDpHNzbGljaCBzaW5kLiAKCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBTY2F0dGVycGxvdAoKZGYgPC0gYXVnbWVudChtZGxfcmFjaW5nKQoKIyBwbG90IHJlc2lkdWFscwpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUmFjaW5nIEdlbnJlIikgKwogIHhsYWIoIiIpCmBgYApgYGB7cn0KIyBDcmVhdGUgUmVzaWR1YWwgSGlzdG9ncmFtICh0byBzZWUgaWYgdGhlIGRhdGEgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uKQoKZ2dwbG90KGRmX3JhY2luZywgYWVzKHggPSBtZGxfcmFjaW5nJHJlc2lkdWFscykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUmFjaW5nIEdlbnJlIikgKwogIHhsYWIoInJlc2lkdWFscyIpCmBgYApNaXQgSGlsZmUgZGVyIHJvdGVuIExpbmllIHNpZWh0IG1hbiwgZGFzcyBkYXMgSGlzdG9ncmFtbSAgbmljaHQgMTAwJSBub3JtYWx2ZXJ0ZWlsdCBpc3QuIEVzIGtvbW10IGplZG9jaCBzY2hvbiBuYWhlIGFuIGVpZSBOb3JtYWx2ZXJ0ZWlsdW5nIGhlcmFuLCB3ZXNoYWxiIHdpciBkYXMgc28gYWt6ZXB0aWVyZW4ga8O2bm5lbi4KCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBTY2F0dGVycGxvdAoKZGYgPC0gYXVnbWVudChtZGxfcnApCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUm9sZS1QbGF5aW5nIEdlbnJlIikgKwogIHhsYWIoIiIpCmBgYApgYGB7cn0KIyBDcmVhdGUgUmVzaWR1YWwgSGlzdG9ncmFtICh0byBzZWUgaWYgdGhlIGRhdGEgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uKQoKZ2dwbG90KGRmX3JvbGUsIGFlcyh4ID0gbWRsX3JwJHJlc2lkdWFscykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUm9sZS1QbGF5aW5nIEdlbnJlIikgKwogIHhsYWIoInJlc2lkdWFscyIpCmBgYApBdWNoIHNpZWh0IG1hbiwgZGFzcyBkYXMgSGlzdG9ncmFtbSAgbmljaHQgMTAwJSBub3JtYWx2ZXJ0ZWlsdCBpc3QuIEVzIGtvbW10IGplZG9jaCBzY2hvbiBuYWhlIGFuIGVpZSBOb3JtYWx2ZXJ0ZWlsdW5nIGhlcmFuLCB3ZXNoYWxiIHdpciBkYXMgc28gYWt6ZXB0aWVyZW4ga8O2bm5lbi4KYGBge3J9CiMgQ3JlYXRlIFJlc2lkdWFsIFNjYXR0ZXJwbG90CgpkZiA8LSBhdWdtZW50KG1kbF9zaW0pCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU2ltdWxhdGlvbiBHZW5yZSIpICsKICB4bGFiKCIiKQpgYGAKYGBge3J9CiMgQ3JlYXRlIFJlc2lkdWFsIEhpc3RvZ3JhbSAodG8gc2VlIGlmIHRoZSBkYXRhIGlzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbikKCmdncGxvdChkZl9zaW0sIGFlcyh4ID0gbWRsX3NpbSRyZXNpZHVhbHMpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKwogIGdlb21fZGVuc2l0eShjb2xvciA9ICJSZWQiKSArCiAgZ2d0aXRsZSgiUmVzaWR1YWxzIE1vZGVsIFNpbXVsYXRpb24gR2VucmUiKSArCiAgeGxhYigicmVzaWR1YWxzIikKYGBgCk1pdCBIaWxmZSBkZXIgcm90ZW4gTGluaWUgc2llaHQgbWFuLCBkYXNzIGRhcyBIaXN0b2dyYW1tICBuaWNodCAxMDAlIG5vcm1hbHZlcnRlaWx0IGlzdC4gRXMga29tbXQgamVkb2NoIHNjaG9uIG5haGUgYW4gZWllIE5vcm1hbHZlcnRlaWx1bmcgaGVyYW4sIHdlc2hhbGIgd2lyIGRhcyBzbyBha3plcHRpZXJlbiBrw7ZubmVuLgoKCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBTY2F0dGVycGxvdAoKZGYgPC0gYXVnbWVudChtZGxfc3BvcnQpCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU3BvcnQgR2VucmUiKSArCiAgeGxhYigiIikKYGBgCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBIaXN0b2dyYW0gKHRvIHNlZSBpZiB0aGUgZGF0YSBpcyBhIG5vcm1hbCBkaXN0cmlidXRpb24pCgpnZ3Bsb3QoZGZfc3BvcnQsIGFlcyh4ID0gbWRsX3Nwb3J0JHJlc2lkdWFscykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU3BvcnQgR2VucmUiKSArCiAgeGxhYigicmVzaWR1YWxzIikKYGBgCk1pdCBIaWxmZSBkZXIgcm90ZW4gTGluaWUgc2llaHQgbWFuLCBkYXNzIGRhcyBIaXN0b2dyYW1tICBuaWNodCAxMDAlIG5vcm1hbHZlcnRlaWx0IGlzdC4gRXMga29tbXQgamVkb2NoIHNjaG9uIG5haGUgYW4gZWllIE5vcm1hbHZlcnRlaWx1bmcgaGVyYW4sIHdlc2hhbGIgd2lyIGRhcyBzbyBha3plcHRpZXJlbiBrw7ZubmVuLgoKIyMjIFdpZSDDpGhubGljaCBzaW5kIHNpY2ggZGllIFZlcmthdWZzemFobGVuIHZvbiBOb3JkYW1lcmlrYSB1bmQgRXVyb3BhPyBVbmQgd2llIHZlcmhhbHRlbiBzaWNoIGRpZSBqYXBhbmlzY2hlbiBWZXJrYXVmc3phaGxlbiBpbSBWZXJnbGVpY2ggenUgZGVuIHp3ZWkgd2VzdGxpY2hlbiBBYnNhdHptw6Rya3Rlbj8KCmBgYHtyfQojIENvbG9yIHRoZSBTUExPTSBvZiBOQV9TYWxlcywgRVVfU2FsZXMsIGFuZCBKUF9TYWxlcyBieSBuaW50ZW5kbwpkZl9jbGVhbiAlPiUKICBwbG90X2x5KGNvbG9yID0gfkdlbnJlKSAlPiUgCiAgYWRkX3RyYWNlKAogICAgdHlwZSA9ICdzcGxvbScsCiAgICBkaW1lbnNpb25zID0gbGlzdCgKICAgICAgbGlzdChsYWJlbCA9ICdOLiBBbWVyaWNhJywgdmFsdWVzID0gfk5BX1NhbGVzKSwKICAgICAgbGlzdChsYWJlbCA9ICdFdXJvcGUnLCB2YWx1ZXMgPSB+RVVfU2FsZXMpLCAgICAKICAgICAgbGlzdChsYWJlbCA9ICdKYXBhbicsIHZhbHVlcyA9IH5KUF9TYWxlcykgICAgICAgCiAgICApCiAgKQpgYGAKw4R1c3NlcnN0IHNwYW5uZW5kIHp1IHNlaGVuIGlzdCwgZGFzcyBzaWNoIGdyb3NzZSBVbnRlcnNjaGllZGUgc2VoZW4gbGFzc2VuLCB3aWUgc2ljaCBiZXN0aW1tdGUgR2VucmUgdmVya2F1ZnQgaGFiZW4uIERpZSBnZW9ncmFwaGlzY2hlIExhZ2Ugdm9uIGRlbSBBYnNhdHptYXJrdCBpc3QgZGFiZWkgZWhlciB6d2VpdHJhbmdpZy4gTWl0IEFic3RhbmQgYW0gaMOkdWZpZ3N0ZW4gd3VyZGVuIEFjdGlvbiBTcGllbGUgdmVya2F1ZnQuIAoKCiMjIyBWZXJrYXVmZW4gc2ljaCBOaW50ZW5kbyBTcGllbGUgd2VsdHdlaXQgYmVzc2VyIGFscyBTcGllbGUgRWxlY3Ryb25pYyBBcnRzLiAKCkRhZsO8ciBtw7xzc2VuIHdpciB6dWVyc3QgdW5zZXIgRGF0YWZyYW1lIEZpbHRlcm4sIGRhbWl0IHdpciBudXJub2NoIFNwaWVsZSB2b24gTmludGVuZG8gdW5kIEVsZWN0cm9uaWMgQXJ0cyBoYWJlbi4KYGBge3J9CiMgQ3JlYXRlIERhdGFGcmFtZSB3aXRoIG9ubHkgTmludGVuZG8gYW5kIFNvbnkgYXMgUHVibGlzaGVyCnB1Ymxpc2hlcnMgPSBjKCJOaW50ZW5kbyIsICJFbGVjdHJvbmljIEFydHMiKQoKZGZfcHVibGlzaGVyIDwtIGRmX2NsZWFuICU+JQogIGZpbHRlcigKICAgIFB1Ymxpc2hlciAlaW4lIHB1Ymxpc2hlcnMKICAgICkKCmRmX3B1Ymxpc2hlcgpgYGAKYGBge3J9CiMgUmVwbGFjZSBwdWJsaXNoZXIgbmFtZSB3aXRoIDAgYW5kIDEKZGZfcHVibGlzaGVyJFB1Ymxpc2hlcltkZl9wdWJsaXNoZXIkUHVibGlzaGVyID09ICJFbGVjdHJvbmljIEFydHMiXSA8LSAwCmRmX3B1Ymxpc2hlciRQdWJsaXNoZXJbZGZfcHVibGlzaGVyJFB1Ymxpc2hlciA9PSAiTmludGVuZG8iXSA8LSAxCgojIFNhdmUgYXMgaW50CmRmX3B1Ymxpc2hlciRQdWJsaXNoZXIgPC0gYXMubnVtZXJpYyhkZl9wdWJsaXNoZXIkUHVibGlzaGVyKQpgYGAKCmBgYHtyfQojIENyZWF0ZSBsb2dpc3RpYyBtb2RlbAptZGxfcHVibGlzaGVyIDwtIGdsbShQdWJsaXNoZXIgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3B1Ymxpc2hlciwgZmFtaWx5ID0gYmlub21pYWwoKSkKYGBgCgpgYGB7cn0KZ2dwbG90KGRmX3B1Ymxpc2hlciwgYWVzKHg9R2xvYmFsX1NhbGVzLCB5PVB1Ymxpc2hlcikpICsgCiAgZ2VvbV9wb2ludChhbHBoYT0uNSwgY29sb3I9IkJsdWUiKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJnbG0iLCBjb2wgPSAiUmVkIiwgc2U9RkFMU0UsIG1ldGhvZC5hcmdzID0gbGlzdChmYW1pbHk9Ymlub21pYWwpKSArCiAgbGFicygKICAgIHggPSAiU2FsZXMgKG1pbGxpb24pIiwKICAgIHkgPSAiMT1OaW50ZW5kbyAvIDA9RWxlY3Ryb25pYyBBcnRzIiwKICAgIHRpdGxlID0gIlByb2JhYmlsaXR5IHRoYXQgYSBnYW1lIGlzIGZyb20gTmludGVuZG8gYmFzZWQgb24gZ2xvYmFsIHNhbGVzIgogICkKYGBgCldpciBrw7ZubmVuIGhpZXIgZ3V0IHNlaGVuLCBkYXNzIE5pbnRlbmRvIHZpZWwgZXJmb2xncmVpY2hlcmUgU3BpZWxlIHByb2R1emllcnQgaGF0LiBEaWVzIGRldXRldCBhdWNoIGFuLCBkYXNzIE5pbnRlbmRvIGJlbGllYnRlciBpc3QgYWxzIEVsZWN0cm9uaWMgQXJ0cy4gCgoKIyMjIFZlcmthdWZlbiBzaWNoIGluIEphcGFuIE5pbnRlbmRvIFdpaSBTcGllbGUgYmVzc2VyIGFscyBOaW50ZW5kbyBEUyBTcGllbGU/CgpOdW4gZmlsdGVybiB3aXIgenVlcnN0IHVuc2VyZW4gRGF0YWZyYW1lIG5hY2ggV2lpIHVuZCBuYWNoIERTIFNwaWVsZW4uCgpgYGB7cn0KIyBDcmVhdGUgRGF0YUZyYW1lIHdpdGggb25seSBXaWkgYW5kIERTIGFzIFBsYXRmb3JtcwpwbGF0Zm9ybXMgPSBjKCJXaWkiLCAiRFMiKQoKZGZfcGxhdGZvcm0gPC0gZGZfY2xlYW4gJT4lCiAgZmlsdGVyKAogICAgUGxhdGZvcm0gJWluJSBwbGF0Zm9ybXMKICAgICkKCmRmX3BsYXRmb3JtCmBgYApgYGB7cn0KIyBSZXBsYWNlIHBsYXRmb3JtIG5hbWUgd2l0aCAwIGFuZCAxCmRmX3BsYXRmb3JtJFBsYXRmb3JtW2RmX3BsYXRmb3JtJFBsYXRmb3JtID09ICJXaWkiXSA8LSAwCmRmX3BsYXRmb3JtJFBsYXRmb3JtW2RmX3BsYXRmb3JtJFBsYXRmb3JtID09ICJEUyJdIDwtIDEKCiMgU2F2ZSBhcyBpbnQKZGZfcGxhdGZvcm0kUGxhdGZvcm0gPC0gYXMubnVtZXJpYyhkZl9wbGF0Zm9ybSRQbGF0Zm9ybSkKYGBgCgpgYGB7cn0KIyBDcmVhdGUgbG9naXN0aWMgbW9kZWwKbWRsX3BsYXRmb3JtIDwtIGdsbShQbGF0Zm9ybSB+IEpQX1NhbGVzLCBkYXRhID0gZGZfcGxhdGZvcm0sIGZhbWlseSA9IGJpbm9taWFsKCkpCmBgYAoKYGBge3J9CmdncGxvdChkZl9wbGF0Zm9ybSwgYWVzKHg9SlBfU2FsZXMsIHk9UGxhdGZvcm0pKSArIAogIGdlb21fcG9pbnQoYWxwaGE9LjIsIGNvbG9yPSJCbHVlIikgKwogIHN0YXRfc21vb3RoKG1ldGhvZD0iZ2xtIiwgY29sID0gIlJlZCIsIHNlPUZBTFNFLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5PWJpbm9taWFsKSkgKwogIGxhYnMoCiAgICB4ID0gIlNhbGVzIChtaWxsaW9uKSIsCiAgICB5ID0gIjE9V2lpIC8gMD1EUyIsCiAgICB0aXRsZSA9ICJQcm9iYWJpbGl0eSB0aGF0IGEgZ2FtZSBpcyBmcm9tIFdpaSBiYXNlZCBvbiBzYWxlcyBpbiBKYXBhbiIKICApCmBgYApBbmhhbmQgdm9uIGRpZXNlbSBQbG90IGthbm4gbWFuIGVya2VubmVuLCBkYXNzIGVpbiBTcGllbCwgd2VsY2hlcyBzaWNoIMO8YmVyIDIgTWlvLiBtYWwgdmVya2F1ZnQgaGF0LCBlaGVyIMO8YmVyIGRpZSBQbGF0Zm9ybSBXaWkgdmVya2F1ZnQgd3VyZGUuIERhcmF1cyBrw7ZubnRlIG1hbiBkZXV0ZW4sIGRhc3MgZGllIE5pbnRlbmRvIFdpaSBpbiBKYXBhbiBiZWxpZWJ0ZXIgaXN0IGFscyBkZXIgTmludGVuZG8gRFMuCgojIyMgRmF6aXQKQmVpIGRlbiBtZWlzdGVuIEdlbnJlbiBpc3QgZXMgbmljaHQgbcO2Z2xpY2ggZGllIFZlcmvDpHVmZSBpbiBFdXJvcGEgYW5oYW5kIGRlciBWZXJrw6R1ZmUgaW4gTm9yZCBBbWVyaWthIHZvcmhlcnp1c2FnZW4uIFdpciBoYWJlbiBqZWRvY2ggZWluaWdlIEdlbnJlbiBnZWZ1bmRlbiwgYmVpIGRlbmVuIFZvcmhlcnNhZ2VuIG3DtmdsaWNoIGlzdDogCgotIFJhY2luZyAvIFJlbm5zcGllbGUKLSBSb2xlLVBsYXlpbmcgLyBSb2xsZW5zcGllbGUKLSBTaW11bGF0aW9uIC8gU2ltdWxhdGlvbnNzcGllbGUKLSBTcG9ydCAvIFNwb3J0c3BpZWxlCgpTZWhyIGludGVyZXNzYW50IHp1IHNlaGVuIHdhciBkYXMgR2VucmUgU2ltdWxhdGlvbi4gVW5zZXIgTW9kZWxsLCB3ZWxjaGVzIGJlaSA5MiUgR2VuYXVpZ2tlaXQgbGllZ3Qgc2FndCB2b3JhdXMsIGRhc3MgZWluIGJlbGllYmlnZXMgU3BpZWwgaW4gRXVyb3BhIGJlc3NlciB2ZXJrYXVmdCB3aXJkIGFscyBpbiBOb3JkIEFtZXJpa2EuIEJlaSBhbGxlbiBhbmRlcmVuIEdlbnJlbiB2ZXJrYXVmZW4gc2ljaCBkaWUgU3BpZWxlIGluIE5vcmQgQW1lcmlrYSBiZXNzZXIu